package m.system.document;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import m.common.action.Action;
import m.common.action.ActionMeta;
import m.common.model.*;
import m.common.model.config.ModelConfig;
import m.common.model.type.FieldType;
import m.system.RuntimeData;
import m.system.exception.MException;
import m.system.lang.HtmlBodyContent;
import m.system.util.AnnotationUtil;
import m.system.util.ClassUtil;
import m.system.util.JSONMessage;
import m.system.util.StringUtil;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class DocumentUtil {
	/**
	 *  返回所有Action文档
	 * 
	 *  MException
	 */
	public static List<JSONObject> documentList() throws MException{
		List<JSONObject> list= new ArrayList<>();
		Map<String,Class<? extends Action>> actionData=RuntimeData.getActionData();
		for(String key : actionData.keySet()){
			Document doc=getDocument(actionData.get(key));
			if(doc.getMethods().length>0){
				list.add(JSONObject.parseObject(JSONObject.toJSONString(doc)));
			}
		}
		return list;
	}
	private static Document getDocument(Class<? extends Action> clazz) throws MException{
		Document doc=new Document();
		ActionMeta meta=AnnotationUtil.getAnnotation4Class(ActionMeta.class, clazz);
		doc.setClassName(clazz.getName());
		doc.setTitle(meta.title());
		if(StringUtil.isSpace(doc.getTitle())){
			doc.setTitle(meta.name());
		}
		doc.setDescription(meta.description());
		doc.setMethods(getMethods("action/" + meta.name(),clazz));
		return doc;
	}
	private static DocumentMethod[] getMethods(String actionPath,Class<? extends Action> clazz) throws MException{
		List<DocumentMethod> list= new ArrayList<>();
		Map<String,DocumentMeta> map=AnnotationUtil.getAnnotationMap4Method(DocumentMeta.class, clazz);
		for(String key : map.keySet()){
			DocumentMeta meta=map.get(key);
			if(null!=meta){
				DocumentMethod method=new DocumentMethod();
				method.setPath(actionPath + "/" + key);
				method.setTitle(meta.method().title());
				method.setDescription(meta.method().description());
				method.setResult(convertResult(meta.method().result()));
				method.setPermission(meta.method().permission());
				method.setParams(getParams(meta.params(),meta.models(),clazz));
				list.add(method);
			}
		}
		return list.toArray(new DocumentMethod[]{});
	}
	private static Object convertResult(String result) {
		result=result.trim();
		Pattern pattern=Pattern.compile("\\#\\{.+?\\}");
		Matcher matcher=pattern.matcher(result);
		while(matcher.find()){
			String str=matcher.group();
			String str1=str.substring(2,str.length()-1);
			result=result.replace(str, toConvert(str1));
		}
		try {
			if (result.indexOf("{") == 0) {
				return JSONObject.parseObject(result);
			} else if (result.indexOf("[") == 0) {
				return JSONArray.parseArray(result);
			}
		}catch (Exception ignored){}
		return result;
	}
	private static String toConvert(String str){
		if(str.equals("code")){
			return "\"0：请求成功，其他为失败\",msg:\"返回消息，非必要参数\"";
		}else{
			try{
				JSONMessage json=new JSONMessage();
				json.push("oid","STRING 50 主键");
				Class<? extends Model> clazz=ClassUtil.getClass(Model.class,str);
				Map<String,FieldMeta> fieldMap=ModelConfig.getFieldMetaMap(clazz);
				for(String key : fieldMap.keySet()){
					FieldMeta fm=fieldMap.get(key);
					json.push(key,fm.type()+" "+(fm.type()==FieldType.STRING?""+fm.length():"")+" "+fm.description()+" "+fm.dictType()+fm.dictOption());
				}
				Map<String,QueryFieldMeta> queryFieldMap=ModelConfig.getQueryFieldMetaMap(clazz);
				for(String key : queryFieldMap.keySet()){
					QueryFieldMeta qf=queryFieldMap.get(key);
					json.push(key,qf.type()+" "+qf.description());
				}
				Map<String,LinkTableMeta> linkTableMap=ModelConfig.getLinkTableMetaMap(clazz);
				for(String key : linkTableMap.keySet()){
					LinkTableMeta lm=linkTableMap.get(key);
					json.push(key,new HtmlBodyContent("{\"oid\":\""+lm.description()+" "+lm.table().getName()+"\"}"));
				}
				return json.toJSONString();
			}catch(Exception ignored){}
		}
		return "\"" + str + "\"";
	}
	private static DocumentParam[] getParams(DocumentParamMeta[] params,DocumentModelMeta[] models,Class<? extends Action> actionClazz) throws MException{
		List<DocumentParam> list= new ArrayList<>();
		for(DocumentParamMeta meta : params){
			DocumentParam param=new DocumentParam();
			param.setName(meta.name());
			param.setDescription(meta.description());
			param.setType(meta.type());
			param.setLength(meta.length());
			param.setNotnull(meta.notnull());
			param.setDictType(meta.dictType());
			param.setDictOption(meta.dictOption());
			list.add(param);
		}
		for(DocumentModelMeta meta : models){
			list.addAll(toParams(meta,actionClazz));
		}
		return list.toArray(new DocumentParam[]{});
	}
	@SuppressWarnings("unchecked")
	private static List<DocumentParam> toParams(DocumentModelMeta meta,Class<? extends Action> actionClazz) throws MException {
		List<DocumentParam> list= new ArrayList<>();
		Class<Model> clazz;
		try {
			clazz=(Class<Model>)ClassUtil.getClass(meta.name());
		} catch (ClassNotFoundException e) {
			throw new MException(DocumentUtil.class,"注解DocumentModelMeta有误!"+meta.name());
		}
		Map<String,LinkTableMeta> linkTableMap=ModelConfig.getLinkTableMetaMap(clazz);
		String[] fs=meta.fieldNames();
		boolean[] ns=meta.notnull();
		int nsl=ns.length;
		for(int i=0,len=fs.length;i<len;i++){
			String field=meta.fieldNames()[i];
			if(field.contains(".")){
				int n=field.indexOf(".");
				String fn=field.substring(0, n);
				String newField=field.substring(n+1);
				list.add(toParam(meta.define() + "." + fn,newField,linkTableMap.get(fn).description(),
					i < nsl && ns[i],linkTableMap.get(fn).table(),actionClazz));
			}else{
				list.add(toParam(meta.define(),field,"",
					i < nsl && ns[i],clazz,actionClazz));
			}
		}
		return list;
	}
	private static DocumentParam toParam(String define,String field,String description,boolean notnull,Class<? extends Model> clazz,Class<? extends Action> actionClazz) throws MException{
		Map<String,FieldMeta> fieldMap=ModelConfig.getFieldMetaMap(clazz);
		Map<String,LinkTableMeta> linkTableMap=ModelConfig.getLinkTableMetaMap(clazz);
		if(field.contains(".")){
			int i=field.indexOf(".");
			String fn=field.substring(0, i);
			String newField=field.substring(i+1);
			return toParam(define + "." + fn,linkTableMap.get(fn).description(),newField,
					notnull,linkTableMap.get(fn).table(),actionClazz);
		}else{
			FieldMeta fm=fieldMap.get(field);
			DocumentParam param=new DocumentParam();
			if(null==fm&&field.equals("oid")){
				param.setName(define + ".oid");
				param.setDescription(description + ".主键");
				param.setType(FieldType.STRING);
				param.setLength(20);
				param.setNotnull(notnull);
				if(DictionaryModel.class.isAssignableFrom(clazz)){
					param.setDictType(clazz.getName());
				}
				return param;
			}else if(null!=fm){
				param.setName(define + "." + field);
				param.setDescription(description+"."+fm.description());
				param.setType(fm.type());
				param.setLength(fm.length());
				param.setNotnull(notnull);
				param.setDictType(fm.dictType());
				param.setDictOption(fm.dictOption());
				return param;
			}else{
				throw new MException(DocumentUtil.class,actionClazz.getName()+"注解DocumentModelMeta有误!"+define+"."+field);
			}
		}
	}
}
